/**
* Copyright (c) 2014 Richard Warburton (richard.warburton@gmail.com)
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
**/
package com.insightfullogic.honest_profiler.ports.sources;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.insightfullogic.honest_profiler.core.MachineListener;
import com.insightfullogic.honest_profiler.core.ThreadedAgent;
import com.insightfullogic.honest_profiler.core.platform.Platforms;
import com.insightfullogic.honest_profiler.core.sources.VirtualMachine;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachineDescriptor;
public class LocalMachineSource
{
private static final String VM_ARGS = "sun.jvm.args";
private static final String AGENT_NAME = "liblagent" + Platforms.getDynamicLibraryExtension();
private static final String USER_DIR = "user.dir";
private static final long DEFAULT_SLEEP_PERIOD = 500;
private final Logger logger;
private MachineListener listener;
private final long sleepPeriod;
private final ThreadedAgent threadedAgent;
private Map<VirtualMachineDescriptor, VirtualMachine> vmMap;
public LocalMachineSource(final Logger logger, final MachineListener listener)
{
this(logger, listener, DEFAULT_SLEEP_PERIOD);
}
public LocalMachineSource(final Logger logger,
final MachineListener listener,
final long sleepPeriod)
{
this.logger = logger;
this.listener = listener;
this.sleepPeriod = sleepPeriod;
vmMap = new HashMap<>();
threadedAgent = new ThreadedAgent(
LoggerFactory.getLogger(ThreadedAgent.class),
this::discoverVirtualMachines);
}
@PostConstruct
public void start()
{
threadedAgent.start();
}
public boolean discoverVirtualMachines()
{
poll();
return sleep();
}
private boolean sleep()
{
try
{
Thread.sleep(sleepPeriod);
return true;
}
catch (InterruptedException e)
{
return false;
}
}
private void poll()
{
Set<VirtualMachineDescriptor> current = new HashSet<>(com.sun.tools.attach.VirtualMachine.list());
difference(current, vmMap.keySet(), this::onNewDescriptor);
difference(new HashSet<>(vmMap.keySet()), current, this::onClosedDescriptor);
}
private void difference(
Set<VirtualMachineDescriptor> left,
Set<VirtualMachineDescriptor> right,
Consumer<VirtualMachineDescriptor> action)
{
// TODO: only attach once per vm
left.stream()
.filter(vm -> !right.contains(vm))
.forEach(action);
}
private void onNewDescriptor(VirtualMachineDescriptor descriptor)
{
VirtualMachine vm = attach(descriptor);
if(vm != null) {
vmMap.put(descriptor, vm);
listener.onNewMachine(vm);
}
}
private void onClosedDescriptor(VirtualMachineDescriptor descriptor)
{
VirtualMachine vm = vmMap.remove(descriptor);
if(vm != null) {
listener.onClosedMachine(vm);
}
}
private VirtualMachine attach(VirtualMachineDescriptor descriptor)
{
try
{
com.sun.tools.attach.VirtualMachine vm = com.sun.tools.attach.VirtualMachine.attach(descriptor);
String vmArgs = vm.getAgentProperties().getProperty(VM_ARGS);
String id = descriptor.id();
String displayName = descriptor.displayName();
boolean agentLoaded = vmArgs.contains(AGENT_NAME);
String userDir = getUserDir(vm);
return new VirtualMachine(id, displayName, agentLoaded, userDir, vmArgs);
}
catch (AttachNotSupportedException e)
{
logger.warn(e.getMessage());
}
catch (IOException e)
{
if (!noSuchProcess(e))
{
logger.warn(e.getMessage(), e);
}
}
return null;
}
private String getUserDir(com.sun.tools.attach.VirtualMachine vm) throws IOException
{
final String userDir = vm.getAgentProperties().getProperty(USER_DIR);
if (userDir != null)
{
return userDir;
}
return vm.getSystemProperties().getProperty(USER_DIR);
}
private boolean noSuchProcess(IOException e)
{
return e.getMessage().contains("No such process");
}
@PreDestroy
public void stop()
{
threadedAgent.stop();
}
}